Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(shape): update typings and add factory functions #776

Merged
merged 15 commits into from
Aug 10, 2020

Conversation

kristw
Copy link
Collaborator

@kristw kristw commented Aug 5, 2020

💥 Breaking Changes

  • N/A

🚀 Enhancements

Extract and export new shape Factories

Create new factory functions for d3-shape and export as part of vx/shape (arc, area, line, pie, radialLine),
similar to vx/scale has factories for d3-scale.

Example

import { arc } from `@vx/shape`;
const path = arc({ 
  innerRadius,
  outerRadius,
  cornerRadius,
});
// is equivalent to
import { arc } from 'd3-shape';
const path = arc()
  .innerRadius(innerRadius)
  .outerRadius(outerRadius)
  .cornerRadius(cornerRadius);

These factories are used to simplify and reduce duplicate implementation in Arc, Pie, etc.

Improve typings

Scale types

Replace custom-defined scale types in vx/shape with types from vx/scale.

Shared types

Previously there is a pattern of component A pick props from B and B pick from C. Chain picking like this can be hard to trace so the common props are extracted as BaseXXXProps and declared in types directory for the component to import and extends.

Consistent Accessor types

Previously each file declare its own accessor types. We had several NumberAccessor which meant different things. This PR refactors and unifies them in types/accessor.ts

Type helpers

Most of the React shape components in this package augment the props with native SVG Props. This pattern was extracted as a helper type and used in all the React shape components.

/**
 * Add fields from `SVGProps` for the specified SVG `Element`
 * to `Props` except fields that already exist in `Props`
 */
export type AddSVGProps<Props, Element extends SVGElement> = Props &
  Omit<React.SVGProps<Element>, keyof Props>;

Default accessors

Default accessors for x, y, source, target are extracted to utils/accessors.ts

Reduce $TSFIXME type

Replace with strict typings when possible.

@kristw kristw force-pushed the kristw--shape2 branch 2 times, most recently from 020d5b1 to a0fb156 Compare August 5, 2020 19:47
@@ -45,3 +45,7 @@ export { default as LinkVerticalStep, pathVerticalStep } from './shapes/link/ste
export { default as LinkRadialStep, pathRadialStep } from './shapes/link/step/LinkRadialStep';
export { default as Polygon, getPoints, getPoint } from './shapes/Polygon';
export { default as Circle } from './shapes/Circle';

// Export factory functions
export * from './types/D3ShapeConfig';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Also export the factories

if (padAngle != null) setNumOrAccessor(arc.padAngle, padAngle);
if (padRadius != null) setNumOrAccessor(arc.padRadius, padRadius);
}: AddSVGProps<ArcProps<Datum>, SVGPathElement>) {
const path = arc({
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Use the factory to create path

if (endAngle != null) setNumOrAccessor(arc.endAngle, endAngle);
if (padAngle != null) setNumOrAccessor(arc.padAngle, padAngle);
if (padRadius != null) setNumOrAccessor(arc.padRadius, padRadius);
}: AddSVGProps<ArcProps<Datum>, SVGPathElement>) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Use helper type

const x1Range = x1Scale.range();
const x1Domain = x1Scale.domain();

const barWidth =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic appears in a few files so it was extracted to util/getBandwidth.ts

@@ -75,7 +66,7 @@ export default function BarStackComponent<Datum, Key extends StackKey = StackKey
const barHeight = (yScale(y0(bar)) || 0) - (yScale(y1(bar)) || 0);
const barY = yScale(y1(bar));
const barX =
'bandwidth' in xScale && typeof xScale.bandwidth === 'function'
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bandwidth is always a function.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm v excited that we are enforcing proper usage of band scales here versus anything before with potential to break 😅 not sure why the function check was there before 🤔

@@ -0,0 +1,9 @@
export type Accessor<Datum, Output> = (d: Datum) => Output;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 All accessor types

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for making these more accurate/pulling them out 💯

@@ -0,0 +1,25 @@
import { $TSFIXME } from '../types';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 Simple default accessors that are used in many places

@@ -55,7 +55,7 @@ describe('<Arc />', () => {
ArcChildren({ children: fn });
const args = fn.mock.calls[0][0];
const keys = Object.keys(args);
expect(keys.includes('path')).toEqual(true);
expect(keys).toContain('path');
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 eslint auto-replace this

@@ -64,7 +65,7 @@ describe('<Area />', () => {
AreaChildren({ children: fn });
const args = fn.mock.calls[0][0];
const keys = Object.keys(args);
expect(keys.includes('path')).toEqual(true);
expect(keys).toContain('path');
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 eslint auto-replace this

xScale.range = () => [0, 100];
xScale.domain = () => [0, 100];
xScale.copy = () => xScale;
const yScale = scaleLinear({ domain: [0, 100], range: [100, 0] });
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💬 replace with real scale

@kristw kristw marked this pull request as ready for review August 5, 2020 22:29
@kristw kristw requested a review from williaster August 6, 2020 16:56
Copy link
Collaborator

@williaster williaster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is another epic improvement, thanks for going through all of @vx/shape, it's a beast 👹

had a couple comments, overall it looks great!

packages/vx-shape/src/shapes/BarStack.tsx Outdated Show resolved Hide resolved
@@ -75,7 +66,7 @@ export default function BarStackComponent<Datum, Key extends StackKey = StackKey
const barHeight = (yScale(y0(bar)) || 0) - (yScale(y1(bar)) || 0);
const barY = yScale(y1(bar));
const barX =
'bandwidth' in xScale && typeof xScale.bandwidth === 'function'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm v excited that we are enforcing proper usage of band scales here versus anything before with potential to break 😅 not sure why the function check was there before 🤔

packages/vx-shape/src/shapes/BarStackHorizontal.tsx Outdated Show resolved Hide resolved
packages/vx-shape/src/shapes/BarStack.tsx Show resolved Hide resolved
packages/vx-shape/src/shapes/Pie.tsx Outdated Show resolved Hide resolved
packages/vx-shape/src/types/base.ts Show resolved Hide resolved
packages/vx-shape/src/types/index.ts Outdated Show resolved Hide resolved
@@ -50,11 +50,6 @@ const PieChildren = ({ children, ...restProps }: Partial<PieProps<Datum>>) =>
);

describe('<Pie />', () => {
beforeEach(() => {
// eslint-disable-next-line @typescript-eslint/unbound-method
global.console.error = jest.fn();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this because nimbus handles this for us?

Copy link
Collaborator Author

@kristw kristw Aug 7, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This console.error is one test I am not sure what is going on. It still throw but console.error was not called. I also question the need to check if console.error is called.

Spent half a day trying to dig into issue. Could not figure out why. I checked the demo and verify that Pie is still working fine, so I would like to drop this.

packages/vx-shape/test/BarStack.test.tsx Show resolved Hide resolved
packages/vx-shape/src/util/getBandwidth.ts Outdated Show resolved Hide resolved
@hshoff hshoff merged commit 2fb2aef into airbnb:master Aug 10, 2020
@hshoff hshoff added this to the 0.0.199 milestone Aug 10, 2020
@kristw kristw deleted the kristw--shape2 branch August 17, 2020 19:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants